#ifndef _IM_SERVER_MAPPED_FILE_
#define _IM_SERVER_MAPPED_FILE_

#include <sys/stat.h>
#include <sys/mman.h> 
#include <errno.h>
#include <string.h>
#include <stdarg.h>
#include <fcntl.h>
// #include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include "base/logging.h"

#include "file_reload_timer.h"

class MMapFile {
 public:
  explicit MMapFile(const char * path) : path_(path), mapped_addr_(NULL), size_(0), modify_time_(0) {
    ReloadFile();
  }

  void ReloadFile() {
    int fd = open(path_.c_str(), O_RDONLY);
    if (fd < 0) {
      LOG_WARN("ReloadFile() open error : " << path_);
      return;
    }
    
    struct stat st;
    int status = fstat(fd, &st);
    if (status < 0) {
      LOG_WARN("ReloadFile() fstat error : " << path_);
      close(fd);
      return;
    }
    if (modify_time_ == st.st_mtime) { // 文件事件戳不变，不重新加载
      close(fd);
      return;
    }

    void * map_addr = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
    if (map_addr == MAP_FAILED) {
      LOG_WARN("ReloadFile() mmap error : " << path_);
      close(fd);
      return;
    }

    if (mapped_addr_) {
      munmap(mapped_addr_, size_);
    }

    LOG_INFO("ReloadFile() success : " << path_  << " size=" << size_);
    mapped_addr_ = reinterpret_cast<char*>(map_addr);
    size_ = st.st_size;
    modify_time_ = st.st_mtime;
    close(fd);
  }

  operator bool() const { return mapped_addr_ != NULL; }
  ~MMapFile() {
    if (mapped_addr_) {
      munmap(mapped_addr_, size_);
    }
  }
  const char * mapped_addr() const {
    return mapped_addr_;
  }
  size_t size() const {
    return size_;
  }
 private:
  std::string path_;
  char * mapped_addr_;
  size_t size_;
  time_t modify_time_;
};

class MMapFileCache {
 public:
  static MMapFileCache& Instance() {
    static MMapFileCache instance;
    return instance; 
  }
  void StartReloadTimer(boost::asio::io_service & ioservice) {
    reload_timer_.reset(new FileReloadTimer(ioservice, 3));
    reload_timer_->Start();
  }
  void Reload() {
    typedef std::map<std::string, MMapFile*>::iterator ItType;
    for(ItType it = cache_.begin(); it != cache_.end(); ++it) {
      it->second->ReloadFile();
    }
  }
  MMapFile* Get(const char * path) {
    std::map<std::string, MMapFile*>::iterator it = cache_.find(path);
    
    if (it != cache_.end()) {
      return it->second;
    }
    MMapFile * mmap_file = new MMapFile(path);
    cache_[path] = mmap_file;
    return mmap_file;
  }
  ~MMapFileCache() {
    // TODO: delete
  } 
 private:
  MMapFileCache() {} 
  std::map<std::string, MMapFile*> cache_;
  boost::shared_ptr<FileReloadTimer> reload_timer_;
};


#endif // _IM_SERVER_MAPPED_FILE_
